import os
import sys
import time
import traceback
from datetime import datetime

import logzero
from logzero import logger
from tqdm import tqdm
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException

from config import REPORT_ERROR_COLLECTION_NAME
from utils import open_driver, track_alert, get_mongo_collection, record_run_status

COLLECTION_NAME = 'bilibili_fanju_list'  # 哔哩哔哩番剧列表爬取的结果
COLLECTION_FEED_DETAIL = 'bilibili_fanju_detail_feed'  # 哔哩哔哩番剧列表页发现待更新的详情
current_module = os.path.splitext(os.path.basename(__file__))[0]
logzero.logfile(f"/tmp/{current_module}.log", maxBytes=30_000_000, backupCount=3, encoding='utf-8')

db = get_mongo_collection(COLLECTION_NAME)
feed_db = get_mongo_collection(COLLECTION_FEED_DETAIL)
error_db = get_mongo_collection(REPORT_ERROR_COLLECTION_NAME)
insert_count = 0


@record_run_status(current_module)
def spider(page_begin=1):
    # 索引页是番剧按照更新时间排序
    index_url = f'http://bangumi.bilibili.com/anime/index#p={page_begin}&v=0&area=&stat=0&y=0&q=0&tag=&t=0&sort=0'
    stop = False
    with open_driver() as driver:
        with track_alert(driver):
            page_num = get_total_page(driver, index_url)
            for p in tqdm(range(page_begin, page_num + 1)):
                if stop:
                    break
                try:
                    data = handle_detail(driver, p)
                    """
                    如果记录不存在直接插入到list，将season_id插入到feed
                    如果记录存在并且状态不一致，更新list，将season_id插入feed
                    如果记录存在并且状态一致，停止。因为索引页是根据更新时间排序的。
                    """
                    stop = store_data(data)
                    if stop:
                        break
                except (NoSuchElementException, StaleElementReferenceException) as e:
                    handle_exception(e, p)
                else:
                    delete_error(p)
            logger.info('完成')
            return insert_count


def get_total_page(driver, index_url):
    driver.get(index_url)
    logger.info(f'访问 {index_url}')
    time.sleep(3)
    a_ele = driver.find_elements_by_css_selector('a.endPage')
    if not a_ele:
        logger.error('未找到页码')
    page_num = int(a_ele[0].text)
    logger.info(f'总共{page_num}页')
    return page_num


def handle_detail(driver, p):
    if p > page_begin:
        next_page_ele = driver.find_element_by_css_selector('div.pagelistbox>a.nextPage')
        next_page_ele.click()
        logger.debug('*' * 50)
        logger.info(f'点击下一页,page={p}')
        time.sleep(3)
    li_eles = driver.find_elements_by_css_selector('ul.v_ul>li')
    for li_ele in li_eles:
        season_id = li_ele.get_attribute('data-season_id')

        img = 'http:' + li_ele.find_element_by_css_selector(
            'div.preview>div.cover>a>img').get_attribute(
            'src')
        a_ele = li_ele.find_element_by_css_selector('div.info_wrp>div.info>a')
        title = a_ele.text
        detail_href = a_ele.get_attribute('href')
        update_status = li_ele.find_element_by_css_selector('p.num').text
        data = {'season_id': season_id, 'img': img, 'title': title,
                "detail_href": detail_href,
                'update_status': update_status,
                'update_time': datetime.now()}
        return data


def store_data(data):
    global insert_count
    stop = False
    season_id = data['season_id']
    update_status = data['update_status']
    old_record = db.find_one({'season_id': season_id})
    if old_record:
        if old_record['update_status'] != update_status:
            logger.info(f'{season_id}状态已更新，插入feed并更新列表状态')
            feed_db.insert_one({'season_id': season_id, 'update_time': datetime.now()})
            db.update(old_record, {'$set': data})
        else:
            logger.info(f'{season_id}已存在且状态一致，停止爬取')
            stop = True
    else:
        logger.info(f'{season_id}不存在，现在插入')
        logger.info(data)
        db.insert_one(data)
        feed_db.insert_one({'season_id': season_id, 'update_time': datetime.now()})
        insert_count += 1
    return stop


def handle_exception(e, p):
    logger.error(e)
    logger.error(traceback.format_exc())
    error_record_condition = {'desp': COLLECTION_NAME, 'page': p}
    error_record = error_db.find_one(error_record_condition)
    if not error_record:
        error_db.insert_one(
            {'desp': COLLECTION_NAME, 'page': p, 'e': e,
             'e_traceback': traceback.format_exc(), 'update_time': datetime.now()})


def delete_error(p):
    error_record_condition = {'desp': COLLECTION_NAME, 'page': p}
    error_record = error_db.find_one(error_record_condition)
    if error_record:
        error_db.delete_one(error_record_condition)
        logger.info('删掉异常记录')


if __name__ == '__main__':
    """
    直接python bilibili_fanju_list.py从第一页开始访问，如果从某页码开始访问在后面直接加参数
    """
    page_begin = int(sys.argv[1]) if len(sys.argv) == 2 else 1
    spider(page_begin)
